package cn.trigram.concurrent;


import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 简单封装线程池相关的方法
 * <ol>
 *     <li>
 *      关闭线程池{@link #gracefulShutdown(ExecutorService, int, TimeUnit)}
 *     </li>
 *     <li>
 *      创建自定义名称线程工厂{@link #buildThreadFactory(String, boolean)}
 *     </li>
 *     <li>
 *      安全化的Runnable({@link SafeRunnable})
 *     </li>
 * </ol>
 *
 */
@Slf4j
public class ThreadPoolUtil {
    /**
     * 打印线程池信息
     *
     * @param executor
     * @return {@link List<String> }
     */
    public static void recordStats(ThreadPoolExecutor executor) {
        StringBuilder builder = new StringBuilder();
        builder.append("核心线程数：").append(executor.getCorePoolSize())
                .append(System.lineSeparator())
                .append("当前线程数：").append(executor.getPoolSize())
                .append(System.lineSeparator())
                .append("允许的最大线程数：").append(executor.getMaximumPoolSize())
                .append(System.lineSeparator())
                .append("曾经的最大线程数：").append(executor.getLargestPoolSize())
                .append(System.lineSeparator())
                .append("正在执行的任务总数（近似值）：").append(executor.getActiveCount())
                .append(System.lineSeparator())
                .append("曾经完成的任务总数（近似值）：").append(executor.getCompletedTaskCount())
                .append(System.lineSeparator())
                .append("曾经执行的任务总数（近似值）：").append(executor.getTaskCount())
                .append(System.lineSeparator())
                .append("空闲线程存留时间（毫秒）：").append(executor.getKeepAliveTime(TimeUnit.MILLISECONDS));
        log.debug("{}", builder);
    }

    /**
     * 按照ExecutorService JavaDoc示例代码编写的Graceful Shutdown方法.
     * <p>
     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
     * <p>
     * 如果1/2超时时间后, 则调用shutdownNow,取消在workQueue中Pending的任务,并中断所有阻塞函数.
     * <p>
     * 如果1/2超时仍然超時，則強制退出.
     * <p>
     * 另对在shutdown时线程本身被调用中断做了处理.
     * <p>
     * 返回线程最后是否被中断.
     * <p>
     * 使用了Guava的工具类
     *
     * @see MoreExecutors#shutdownAndAwaitTermination(ExecutorService, long, TimeUnit)
     */
    public static boolean gracefulShutdown(@Nullable ExecutorService threadPool,
                                           int shutdownTimeoutMills) {

        return threadPool == null
                || MoreExecutors
                .shutdownAndAwaitTermination(threadPool, shutdownTimeoutMills, TimeUnit.MILLISECONDS);
    }

    /**
     * @see #gracefulShutdown(ExecutorService, int)
     */
    public static boolean gracefulShutdown(@Nullable ExecutorService threadPool, int shutdownTimeout,
                                           TimeUnit timeUnit) {

        return threadPool == null || MoreExecutors
                .shutdownAndAwaitTermination(threadPool, shutdownTimeout, timeUnit);
    }

    /**
     * 创建ThreadFactory，使得创建的线程有自己的名字而不是默认的"pool-x-thread-y"
     * <p>
     * 使用了Guava的工具类
     *
     * @see ThreadFactoryBuilder#build()
     */
    public static ThreadFactory buildThreadFactory(String threadNamePrefix) {

        return new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").build();
    }

    /**
     * 可设定是否daemon, daemon线程在主线程已执行完毕时, 不会阻塞应用不退出, 而非daemon线程则会阻塞.
     *
     * @see #buildThreadFactory(String)
     */
    public static ThreadFactory buildThreadFactory(String threadNamePrefix,
                                                   boolean daemon) {

        return new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").setDaemon(daemon)
                .build();
    }

    /**
     * 防止用户没有捕捉异常导致中断了线程池中的线程, 使得SchedulerService无法继续执行.
     * <p>
     * 在无法控制第三方包的Runnable实现时，调用本函数进行包裹.
     */
    public static Runnable safeRunnable(Runnable runnable) {

        return new SafeRunnable(runnable);
    }

    /**
     * 保证不会有Exception抛出到线程池的Runnable包裹类，防止用户没有捕捉异常导致中断了线程池中的线程, 使得SchedulerService无法执行.
     * 在无法控制第三方包的Runnable实现时，使用本类进行包裹.
     */
    private static class SafeRunnable implements Runnable {

        private static final Logger logger = LoggerFactory.getLogger(SafeRunnable.class);

        private final Runnable runnable;

        public SafeRunnable(Runnable runnable) {

            Validate.notNull(runnable);
            this.runnable = runnable;
        }

        @Override
        public void run() {

            try {
                runnable.run();
            } catch (Throwable e) {
                // 仅捕获异常，因为定时线程（scheduled thread ）会因为异常抛出而中断执行
                logger.error("Unexpected error occurred in task", e);
            }
        }

    }

}
